Implement type checking for expressions and statements (Project 4)#3
Conversation
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
|
@copilot int main() { |
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
Fixed in c86ae84. The issue was that the grammar didn't support comma-separated variable declarations like |
|
@copilot 给我用中文详细讲讲compiler在本次要求中具体做了哪些修改,分别对应的项目要求是什么 |
本次Project 4的Compiler主要做了以下修改: 1. 表达式类型检查系统 (对应2.2节)新增 private static class ExprInfo {
public final Type type;
public final boolean isLvalue;
}新增
2. 空指针处理 (对应2.3节)常量0可以作为空指针,只在以下情况允许:
通过 3. 语句类型检查 (对应2.4节)
4. 语法扩展:逗号分隔的变量声明修改了 5. 错误处理机制使用 |
|
@copilot 5 对 Project 3 中 structure tag scope 的勘误 |
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
已在 cb0ad24 修复。修改内容:
现在以下两个测试用例都能正确通过: // 测试1: b0.ptr2->qwq 不再报 incomplete type 错误
struct b { struct b *ptr1; struct a *ptr2; };
struct a { struct b b0; int qwq; };
int main0() { struct b b0; b0.ptr2->qwq = 1; return 0; }
// 测试2: struct a a0 不再报 Definition of incomplete type 错误
struct b { struct b *ptr1; struct a { int a; int b; } *ptr2; };
struct a a0; |
|
@copilot /Users/wuzhi/Desktop/MyProgram/CS323-Compilers-2025F-Projects/src/main/java/impl/Compiler.java:629:68 |
这个错误是因为 请在项目根目录运行以下命令: make clean
make这会重新生成ANTLR解析器。 如果没有 java -jar libs/antlr-4.13.2-complete.jar -visitor -o src/main/java/generated/Splc -package generated.Splc Splc.g4 |
|
@copilot 哦哦哦,我需要的是g4文件不更改的版本,不需要支持逗号分隔的变量声明,g4文件回退成这个就好://lexer grammar Splc; // Changes in Project 2: Splc.g4 contains both parser rules and lexer rules. // IDEA Plugin Settings // ========================= program: globalDef* EOF; globalDef specifier varDec funcArgs statement expression // ========================= // ---------- Keywords ---------- // ---------- Operators ---------- // ---------- Separators ---------- // ---------- Identifiers & Literals ---------- // ---------- Whitespace & Comments ---------- LINE_COMMENT BLOCK_COMMENT |
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
|
@copilot 你做的很好,但是现在代码经过测试后只有64分。接下来请给我尽可能全面的样例,来确保我的代码的完备性,注意所有测试样例都必须符合g4文件的规定,比如说不能用“,”连续定义变量。 |
以下是覆盖 Project 4 各项要求的全面测试样例: 1. 标识符检查 [2.2.1]int foo() { return 0; }
int main() {
foo; // Error: identifier is function, not variable
return 0;
}2. 函数调用 [2.2.2]int add(int a, int b) { return a + b; }
int main() {
int x;
add(1); // Error: wrong param count
add(1, 2, 3); // Error: wrong param count
add(x, x); // OK
return 0;
}3. 数组访问 [2.2.3]int main() {
int arr[10];
int *p;
arr[0] = 1; // OK: array lvalue
p[0] = 1; // OK: pointer
arr[p] = 1; // Error: index not integer
return 0;
}4. 结构体访问 DOT [2.2.4]struct Point { int x; int y; };
int main() {
struct Point p;
p.x = 1; // OK
p.z = 1; // Error: bad member
return 0;
}5. 指针访问 ARROW [2.2.5]struct Point { int x; int y; };
int main() {
struct Point *p;
p->x = 1; // OK
p->z = 1; // Error: bad member
return 0;
}6. 取地址 [2.2.6]int main() {
int x;
int *p;
p = &x; // OK
p = &1; // Error: lvalue required
p = &(x + 1); // Error: lvalue required
return 0;
}7. 解引用 [2.2.7]int main() {
int x;
int *p;
*p = 1; // OK
*x = 1; // Error: not pointer type
return 0;
}8. 自增自减 [2.2.8]int main() {
int x;
int *p;
x++; // OK
p++; // OK
1++; // Error: lvalue required
return 0;
}9. 加减法和指针算术 [2.2.9]int main() {
int x;
int *p1;
int *p2;
x = p1 + 1; // Error: ptr + int = ptr, not int
p1 = p1 + 1; // OK
p1 = 1 + p1; // OK
x = p1 - p2; // OK: ptr - ptr = int
p1 = p1 + p2; // Error: ptr + ptr not allowed
return 0;
}10. 算术运算 [2.2.10]int main() {
int x;
int *p;
x = x * 2; // OK
x = p * 2; // Error: pointer in arithmetic
x = x / p; // Error: pointer in arithmetic
return 0;
}11. 比较运算 [2.2.11]int main() {
int x;
int *p;
x = (x < 1); // OK
x = (p < p); // Error: pointer in comparison
return 0;
}12. 相等比较 [2.2.12]int main() {
int x;
int *p;
int arr[10];
x = (x == 1); // OK
x = (p == p); // OK: same type
x = (p == 0); // OK: null pointer
x = (p == x); // Error: different types
x = (arr == arr); // Error: array type
return 0;
}13. 逻辑运算 [2.2.13]int main() {
int x;
int *p;
int arr[10];
x = !x; // OK
x = !p; // OK: pointer in logic
x = (x && p); // OK
x = !arr; // Error: array type
return 0;
}14. 赋值 [2.2.14]int main() {
int x;
int *p;
int arr[10];
x = 1; // OK
1 = x; // Error: lvalue required
p = 0; // OK: null pointer
p = 1; // Error: int to pointer
arr = arr; // Error: array assignment
return 0;
}15. 返回语句 [2.4]int foo() {
int *p;
return p; // Error: return type mismatch
}
int main() { return 0; }16. 条件语句 [2.4]int main() {
int arr[10];
if (arr) {} // Error: array in condition
while (arr) {} // Error: array in condition
return 0;
}17. 局部变量初始化 [2.4]int main() {
int *p;
int x = p; // Error: type mismatch
return 0;
}18. 结构体指针完整性测试struct b { struct a *ptr; };
struct a { int x; };
int main() {
struct b b0;
b0.ptr->x = 1; // OK: struct a is complete
return 0;
}这些测试样例涵盖了文档中2.2节和2.4节的所有要求。每个样例都标注了预期结果(OK或Error)。 |
|
@copilot 这次在测试后文档要求补充说明了可能有歧义的地方,请检查这些地方并确定是否需要改正:[2.2.9] 加减法(rvalue):若 加法、减法 两侧的 Operand 均为整型,则其结果为整型。v4 修改: |
… type errors Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
已在 6172f6d 中修复。主要改动:
|
|
@copilot int *a; |
|
@copilot int *a; |
Co-authored-by: wuzhi456 <217564920+wuzhi456@users.noreply.github.com>
Project 4 - Semantic Check Part 2 Implementation
This PR implements comprehensive type checking for expressions and statements as specified in Project 4 requirements.
Changes
Expression type system
ExprInfoclass tracks type and value category (lvalue/rvalue)checkExpression()validates all expression forms with proper type constraintsOperator type checking
Statement validation
unmatchedTypeForBinaryOPfor errorsStructure tag scope fix (C standard compliance)
addTag()andupdateTag()to always add/update tags at file scopeFILE_SCOPE_IDconstant andgetFileTagScope()helper method with O(1) accessError handling
Project4Exceptionintegration with semantic error templatesbadParamCount), then types (badParamType) with 1-based indexingNote: Grammar file (Splc.g4) is kept unchanged - no comma-separated variable declaration support.
Original prompt
Project 4 - Semantic Check Part 2
2025 年 11 月 17 日
1 项目要求
在 Project 3 中,你已经完成了符号表和对类型的表示。在 Project 4 中,你需要基于上述类型系统和符号
表完成语义分析中的类型检查。
1.1 项目假设
在 Project 4 中,我们继承来自 Project 3 的如下假设:
• 样例可能存在语义错误,但不存在 Project 3 中规定的语义错误;
• specifier 中的完整结构体规则出现时,它在语法树中一定是全局变量定义或全局结构体声明的任意次
子节点,即它不会出现在函数参数(包含函数声明和函数定义)、函数体内;
并新增如下假设:
• specifier 不再能够被推导为 CHAR,即 char 类型不再会出现;同时,字符常量 Char 也不会出现。
• 样例中出现的结构体均是被完整声明过的,即不存在 incomplete structure。
• 函数声明与 函数定义一定相符,即函数的返回类型与参数列表一定相同,你不需要进行额外检查。
• 函数的返回类型只能为 int。
1.2 扩展要求
扩展部分在每个 Project 之间都是独立计分的,在后续的 Project 中移除对某扩展部分的支持不影响前
序 Project 的分数。也就是说,你可以选择在前面的 Project 中完成较为简单的扩展任务;如果你发现
在后续的 Project 中完成扩展部分过于困难,你可以选择不完成后续 Project 的扩展部分,这样不会影
响你前面 Project 的分数。
本项目的扩展部分与前序 Project 保持一致,你需要确保你的类型检查能够正确处理结构体和指针相关的
语法。
1
2 类型检查
2.1 类型相等
对于两个类型,它们被视为相等/相同,若它们均是:
• int;
• array,并且它们的 element type 相等。
• structure,并且它们是同一个结构体类型。
• pointer,并且它们的 reference type 相等。
2.2 expression 的语义与约束
每个 expression 具有两种属性:类型和值分类(Value Category, (表格中简写为 VC), lvalue 或 rvalue);
并且,我们对于其 operand 也有要求。
Rule Return Type VC Constr.
标识符 数字常量 括号 函数调用 数组访问 结构体访问 结构体指针访问 取地址 解引用 后缀自增、后缀自减、前缀自增、前缀自减 加法、减法 一元正号、一元负号、乘法、除法、取模 大于等于、大于、小于、小于等于 等于、不等 逻辑非、逻辑或、逻辑与 赋值 与其声明一致 int 与内部 expression 一致 函数返回类型 T(见下) 该结构体成员的类型 该结构体成员的类型 T →pointer to T pointer to T →T 与 Operand 一致(整型或指针) 见下 整型 整型 整型 整型 与右侧 Operand 一致 lvalue rvalue
与内部一致
rvalue lvalue lvalue lvalue rvalue lvalue rvalue rvalue rvalue rvalue rvalue rvalue rvalue [2.2.1]
[2.2.2]
[2.2.3]
[2.2.4]
[2.2.5]
[2.2.6]
[2.2.7]
[2.2.8]
[2.2.9]
[2.2.10]
[2.2.11]
[2.2.12]
[2.2.13]
[2.2.14]
• [2.2.1] Identifier 所引用的是一个 object (变量),而不是函数。
• [2.2.2] Identifier 所引用的应是一个函数(可以被声明但是未被定义过),参数的类型应该与其声明一致。
• [2.2.3] 数组访问:第一个 Operand 的类型应为 array of T 或者 pointer to T,若类型为 array,则要求它
为 lvalue,第二个 Operand 应该为整型。
• [2.2.4] 结构体访问:第一个 Operand 的类型应为 complete structure type,并且为 lvalue,Identifier 应是
该结构体的成员。
2
• [2.2.5] 结构体指针访问:第一个 Operand 的类型应为 pointer to complete structure type,Identifier 应是
该结构体的成员。
• [2.2.6] 取地址:Operand 应为一个 lvalue。
• [2.2.7] 解引用:Operand 应为一个指针类型。
• [2.2.8] 自增、自减:Operand 应是整型或指针,以及是一个 lvalue。指针对整数的加减法的语义见下。
• [2.2.9] 加减法:若 加法、减法 两侧的 Operand 均为整型,则其结果为整型。
对于 加法和减法,若其一侧 Operand 为指针,一侧 Operand 为整型,则结果为指针 Operand 的类型,其
语义为该指针后第 n 个元素的地址。
对于 减法,若两侧均为同类型指针,则结果为整型,其语义为两个指针之间相差多少个该类型的对象。
否则,是语义错误。
• [2.2.10] 算数运算:Operand 应是整型。
• [2.2.11] 比较:Operand 应是整型。
• [2.2.12] 相等比较:Operand 应是整型或指针,并且两个 Operand 类型相同。
• [2.2.13] 逻辑运算:Operand 应是整型或指针。指针在逻辑运算中的语义为与 0 比较,非 0 则为真。
• [2.2.14] 赋值: Operand 应是整型或指针,并且两个 Operand 类型相同。左侧 Operand 应该是 lvalue。该
表达式的返回值为右侧 Operand 的值。
2.3 0 与 null
在某些条件下,数字常量 0 可以被视为一个指针,它代表空指针。
在我们的 Project 中,上述条件被限制为:直接1出现在赋值的右侧,或者在相等比较的两侧。
示例 1
int main() {
int *p;
p = 0; // OK
p = 20; // Error
}
2.4 statement 的语义和约束
• 局部变量定义:若 ASSIGN expression 子句存在,该 expression 的类型应该与该变量的类型一致。
• 返回语句:返回值的类型应该与函数定义中的返回类型一致。
• If 语句与 While 语句:其中 expression 的类型要求为整型或指针。
1 指该 expression 被推导为嵌套 0 个或多个括号的数字常量 0,而不能经过其他任何 expression 产生式。
3
3 Project 4 语义检查实现
你需要完成 impl.Compiler 类,基础代码已经给出,注意不要绕过 Grader 打印内容。
若你需要修改 framework 包下的文件,请确保你的程序行为不依赖于你所修改的部分。在测评时,
framework 包下所有文件均会被删除,然后替换为我们提供的版本。
3.1 报告语义错误
每个样例文件中可能存在零个、一个或多个语义错误。但是,每个 statemennt SubRule 下,至多存在一
个语义错误。
你需要遍历所有 statemennt,并对其中每个 expression 按照要求进行语义检查,汇报遇到的语义错误。
当你遇到一个语义错误时,你可以停止对整个 expression 树的剩余处理。
即你不需要考虑如何进行错误恢复,遇到错误时直接停止整个 expression 的检查,然后开始下一个
statemennt 的检查即可。
与 Project 3 类似,所有错误报告函数均已预定义在 framework.project4.Project4SemanticError 下。
额外的,我们预定义了一种异常 Project4Exception,你可以在内部处理 expression 遇到语义错误时抛出它,
在遍历 statement 处捕获它并向 Grader 汇报,然后开始处理下一个 statemennt。
Project 4 核心逻辑示例
public class ExprVisitor extends SplcBaseVisitor {
@OverRide
public Void visitExprID(SplcParser.ExprIDContext ctx) {
String id = ctx.Identifier().getText();
// ...
Project4SemanticError.identifierNotVariable(ctx, id).throwException();
// ...
}
}
// in Your Compiler class:
new SplcBaseVisitor() {
@OverRide
public Void visitStmtExpr(SplcParser.StmtExprContext ctx) {
try {
new ExprVisitor().visit(ctx.expression());
} catch (Project4Exception ex) {
grader.reportSemanticError(ex);
}
return null;
}
}.visit(programContext);
4
3.2 预定义的语义错误模板
Project4SemanticError 中定义了如下语义错误的模板:
• identifierNotVariable、identifierNotFunction:当我们期望一个 Identifier 表示变量或函数时,但
是它不是。
• unexpectedType:Operator 要求 Operand(s) 是特定的类型,但给定的类型不符合文档要求。例如:
− 结构体访问左侧是一个 array type.
− 加减法左侧/右侧是一个 array type.
− 一元正号、乘法等算术运算中出现非整型的类型。
• unmatchedTypeForBinaryOP:对于较为复杂的二元运算符,若左侧与右侧的 Operand 类型无法匹配。该
错误仅适用于 加减法 [2.2.9]、相等比较 [2.2.12]、赋值 [2.2.14] 。例如:
− 对于加法,左右侧分别是 pointer type 与 integer 是可以接受的;但是左右侧同时是 pointer type 则
是不可接受的。
− 对于减法,左右侧都是 pointer type,但是所指向的类型不一致,也是不可接受的。
• badParamType、badParamCount:在函数调用中,传入的参数个数与声明不符,以及第 n 个参数类型不
符。
• badMember:在结构体操作中,所给定的 Identifier 不是该结构体类型的成员。
• lvalueRequired:某些操作符只能作用于 lvalue 的操作数,但是操作数为 rvalue。
上述错误模板函数中的第一个参数均是 SplcParser.ExpressionContext ctx,该参数的目的是获取行号
用于打印,而我们规定:样例中,每个 statemennt 中的 expression 均位于同一行。
所以,你不需要纠结应该传入哪一个 expression 对象(root、lhs 或 rhs),传哪个对于行号来说都是一样
的。
4 评分
本次 Project 满分 100 分,其中基础部分 80 分,扩展 1 结构体 10 分,扩展 2 指针 10 分。
我们将运行 framework.project3.Grader,并比对你的程序输出...
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.